/**************************************************************
* M3D_VECT.H   Vectors & Points
**************************************************************/
#if defined _AP_XENON
   #include <ap_comm_xenon.h>
#endif




// Main class definitions
struct m2dV {
   float  x;
   float  y;

   void Set(float x, float y);

   bool operator < (const m2dV & p) const;
   bool operator == (const m2dV & p) const;
   bool operator != (const m2dV & p) const;

   float operator[] (int i) const;
   float & operator[] (int i);

   m2dV & operator+= (const m2dV& v);
   m2dV & operator-= (const m2dV& v);
   m2dV & operator*= (float f);
   m2dV & operator/= (float f);
   m2dV operator- (void) const;

   friend m2dV operator+ (const m2dV& u, const m2dV& v);
   friend m2dV operator- (const m2dV& u, const m2dV& v);
   friend m2dV operator* (float f, const m2dV& v);
   friend m2dV operator* (const m2dV& v, float f);
   friend m2dV operator/ (const m2dV& v, float f);
};

struct m3dV {
   float  x;
   float  y;
   float  z;

   void Set(float x, float y, float z);
   float* Ptr(void) { return &x; }
   const float* Ptr(void) const { return &x; }

   bool operator < (const m3dV & p) const;
   bool operator == (const m3dV & p) const;
   bool operator != (const m3dV & p) const;

   float operator[] (int i) const;
   float & operator[] (int i);

   m3dV & operator+= (const m3dV& v);
   m3dV & operator-= (const m3dV& v);
   m3dV operator- (void) const;
   m3dV & operator-= (float f);
   m3dV & operator*= (float f);
   m3dV & operator/= (float f);

   friend m3dV operator+ (const m3dV& u, const m3dV& v);
   friend m3dV operator- (const m3dV& u, const m3dV& v);
   friend m3dV operator* (const m3dV& u, const m3dV& v);
   friend m3dV operator/ (const m3dV& u, const m3dV& v);
   friend m3dV operator* (float f, const m3dV& v);
   friend m3dV operator* (const m3dV& v, float f);
   friend m3dV operator/ (const m3dV& u, float f);
};

struct m2dVINT {
   int   x;
   int   y;
};

struct m3dVINT {
   int   x;
   int   y;
   int   z;
};

struct m4dVINT {
   int   x;
   int   y;
   int   z;
   int   w;
};


struct m4dV : public m3dV {
   float  w;

   bool operator < (const m4dV & p) const;
   bool operator == (const m4dV & p) const { return x == p.x && y == p.y && z == p.z && w == p.w; }
   bool operator != (const m4dV & p) const { return x != p.x || y != p.y || z != p.z || w != p.w; }

   const m3dV& operator = (const m3dV &src) { *((m3dV*)this) = src; return *this; }

   float & operator [] (int index) { STRONG_ASSERT(index >= 0 && index < 4); return ((float *)this)[index]; }
   float operator [] (int index) const { STRONG_ASSERT(index >= 0 && index < 4); return ((float *)this)[index]; }
   void Set(float _x, float _y, float _z, float _w) { x = _x; y = _y; z = _z; w = _w; }
   void Set(float v) { x = v; y = v; z = v; w = v; }
};

// Texture coordinates
struct m3dVTX {
   float  s;
   float  t;

   bool operator < (const m3dVTX & p) const;
   bool operator == (const m3dVTX & p) const { return s == p.s && t == p.t; }
   bool operator != (const m3dVTX & p) const { return s != p.s || t != p.t; }
};

struct m3dPLANE {
   m3dV   point;  // plane point
   m3dV   normal; // plane normal
};

// convert point representations
#define m2dVectComponent(p,i) (((float*)(p))[i])
#define m3dVectComponent(p,i) (((float*)(p))[i])

#define m2dVIntComponent(p,i) (((int*)(p))[i])
#define m3dVIntComponent(p,i) (((int*)(p))[i])

// External variables (need to be declared here, because m3dVUnitY is used in m3dNormalize()
extern m3dV    m3dVUnitX;
extern m3dV    m3dVUnitY;
extern m3dV    m3dVUnitZ;
extern m3dV    m3dVZero;
extern m3dV    m3dVOneXYZ;
extern m3dV    m3dVUndef;
extern m3dV    m3dVInfinity;

extern m3dV    m3dVUnitXNeg;
extern m3dV    m3dVUnitYNeg;
extern m3dV    m3dVUnitZNeg;

extern m2dV    m2dVUnitX;
extern m2dV    m2dVUnitY;
extern m2dV    m2dVZero;
extern m2dV    m2dVOneXY;
extern m2dV    m2dVUndef;

extern m3dVINT m3dVIntZero;


// 3D-2D Vectors conversions
__INLINE void m3xyV3DToV2D (const m3dV * __RESTRICT pV3D, m2dV * __RESTRICT pV2D)
{
   pV2D->x = pV3D->x; 
   pV2D->y = pV3D->y;
}
__INLINE void m3xzV3DToV2D (const m3dV * __RESTRICT pV3D, m2dV * __RESTRICT pV2D )
{
   pV2D->x = pV3D->x;
   pV2D->y = pV3D->z;
}
__INLINE void m3yzV3DToV2D (const m3dV * __RESTRICT pV3D, m2dV * __RESTRICT pV2D )
{
   pV2D->x = pV3D->y;
   pV2D->y = pV3D->z;
}
__INLINE void m3xzV2DToV3D (const m2dV * __RESTRICT pV2D, m3dV * __RESTRICT pV3D )
{
   pV3D->x = pV2D->x;
   pV3D->z = pV2D->y;
   pV3D->y = 0.f;
}

// Compare points
__INLINE BOOL m3xzIsEqualPoint_A (const m3dV * __RESTRICT p1, const m3dV * __RESTRICT p2, float acc)
{
   return m3dIsZero_A((p1)->x - (p2)->x,acc) && m3dIsZero_A((p1)->z - (p2)->z,acc);
}
__INLINE BOOL m3xyIsEqualPoint_A (const m3dV * __RESTRICT p1, const m3dV * __RESTRICT p2, float acc )
{
   return m3dIsZero_A((p1)->x - (p2)->x,acc) && 
          m3dIsZero_A((p1)->y - (p2)->y,acc);
}
__INLINE BOOL m3dIsEqualPoint_A (const m3dV * __RESTRICT p1, const m3dV * __RESTRICT p2, float acc)
{
   return m3dIsZero_A((p1)->x - (p2)->x,acc) && 
          m3dIsZero_A((p1)->y - (p2)->y,acc) &&
          m3dIsZero_A((p1)->z - (p2)->z,acc);
}
__INLINE BOOL m4dIsEqualPoint_A (const m4dV * __RESTRICT p1, const m4dV * __RESTRICT p2, float acc)
{
   return m3dIsZero_A((p1)->x - (p2)->x,acc) && 
          m3dIsZero_A((p1)->y - (p2)->y,acc) &&
          m3dIsZero_A((p1)->z - (p2)->z,acc) && 
          m3dIsZero_A((p1)->w - (p2)->w,acc);
}

__INLINE BOOL m2dIsEqualPoint_A (const m2dV * __RESTRICT p1, const m2dV * __RESTRICT p2, float acc)
{
   return m3dIsZero_A((p1)->x - (p2)->x,acc) && m3dIsZero_A((p1)->y - (p2)->y,acc);
}

#define m3dIsEqualPoint(p1,p2)   m3dIsEqualPoint_A(p1,p2,M3D_EPSILON)
#define m4dIsEqualPoint(p1,p2)   m4dIsEqualPoint_A(p1,p2,M3D_EPSILON)
#define m3xzIsEqualPoint(p1,p2)  m3xzIsEqualPoint_A(p1,p2,M3D_EPSILON)
#define m3xyIsEqualPoint(p1,p2)  m3xyIsEqualPoint_A(p1,p2,M3D_EPSILON)
#define m2dIsEqualPoint(p1,p2)   m2dIsEqualPoint_A(p1,p2,M3D_EPSILON)

__INLINE bool m3dIsUndef(const m3dV * __RESTRICT v)
{
   return v->x==M3D_FLOAT_UNDEF && v->y==M3D_FLOAT_UNDEF && v->z==M3D_FLOAT_UNDEF;
}

__INLINE bool m2dIsUndef(const m2dV * __RESTRICT v)
{
   return v->x==M3D_FLOAT_UNDEF && v->y==M3D_FLOAT_UNDEF;
}

// Distance between points 
__INLINE float m3xzDist (const m3dV * __RESTRICT p1, const m3dV * __RESTRICT p2)
{
   m3dCheckValid(p1->x);
   m3dCheckValid(p1->z);
   m3dCheckValid(p2->x);
   m3dCheckValid(p2->z);
   return m3dSqrt(m3dPow2((p1)->x-(p2)->x) + m3dPow2((p1)->z-(p2)->z));
}
__INLINE float m3dDist (const m3dV * __RESTRICT p1, const m3dV * __RESTRICT p2)
{
   m3dCheckValid(p1);
   m3dCheckValid(p2);
   return m3dSqrt(m3dPow2((p1)->x-(p2)->x) + m3dPow2((p1)->y-(p2)->y) + m3dPow2((p1)->z-(p2)->z));
}
__INLINE float m2dDist (const m2dV * __RESTRICT p1, const m2dV * __RESTRICT p2)
{
   m3dCheckValid(p1);
   m3dCheckValid(p2);
   return m3dSqrt(m3dPow2((p1)->x-(p2)->x) + m3dPow2((p1)->y-(p2)->y));
}

__INLINE float m3xzDist_2(const m3dV * __RESTRICT p1, const m3dV * __RESTRICT p2)
{
   m3dCheckValid(p1->x);
   m3dCheckValid(p1->z);
   m3dCheckValid(p2->x);
   m3dCheckValid(p2->z);
   return m3dPow2((p1)->x-(p2)->x) + m3dPow2((p1)->z-(p2)->z);
}
__INLINE float m3dDist_2(const m3dV * __RESTRICT p1, const m3dV * __RESTRICT p2)
{
   m3dCheckValid(p1);
   m3dCheckValid(p2);
   return m3dPow2((p1)->x-(p2)->x) + m3dPow2((p1)->y-(p2)->y) + m3dPow2((p1)->z-(p2)->z);
}
__INLINE float m2dDist_2(const m2dV * __RESTRICT p1, const m2dV * __RESTRICT p2)
{
   m3dCheckValid(p1);
   m3dCheckValid(p2);
   return m3dPow2((p1)->x-(p2)->x) + m3dPow2((p1)->y-(p2)->y);
}

// 3D vector length calculation
__INLINE float m3dLengthVector (const m3dV * __RESTRICT v)
{
   m3dCheckValid(v);
#ifdef M3D_USE_SSE
   // Using SSE scalars throughout is about 15-20% faster than using FPU then going to SSE m3dSqrt
   // Note intrinsics are generally faster than asm as they inline and compiler can do better register allocation
   __m128 mx = _mm_load_ss(&v->x);
   __m128 my = _mm_load_ss(&v->y);
   __m128 mz = _mm_load_ss(&v->z);
   float r;
   _mm_store_ss(&r, _mm_sqrt_ss(_mm_add_ss(_mm_add_ss(_mm_mul_ss(mx,mx), _mm_mul_ss(my,my)), _mm_mul_ss(mz,mz))));
   return r;
#else
   return m3dSqrt(m3dPow2((v)->x) + m3dPow2((v)->y) + m3dPow2((v)->z));
#endif
}
__INLINE float m3dLengthVector_2(const m3dV * __RESTRICT v)
{
   m3dCheckValid(v);
   return m3dPow2((v)->x) + m3dPow2((v)->y) + m3dPow2((v)->z);
}
__INLINE float m3dLengthVector_OO (const m3dV * __RESTRICT v)
{
   m3dCheckValid(v);
   return m3dOOSqrt(m3dPow2((v)->x) + m3dPow2((v)->y) + m3dPow2((v)->z));
}
__INLINE float m3xzLengthVector (const m3dV * __RESTRICT v)
{
   m3dCheckValid(v->x);
   m3dCheckValid(v->z);
   return m3dSqrt(m3dPow2((v)->x) + m3dPow2((v)->z));
}
__INLINE float m3xzLengthVector_2 (const m3dV * __RESTRICT v)
{
   m3dCheckValid(v->x);
   m3dCheckValid(v->z);
   return m3dPow2((v)->x) + m3dPow2((v)->z);
}

// 2D vector length computation
__INLINE float m2dLengthVector (const m2dV * __RESTRICT v)
{
   m3dCheckValid(v);
   return m3dSqrt(m3dPow2(v->x) + m3dPow2(v->y));
}
__INLINE float m2dLengthVector_2 (const m2dV * __RESTRICT v)
{
   m3dCheckValid(v);
   return m3dPow2(v->x) + m3dPow2(v->y);
}

// Linear combinations of two vectors
__INLINE void _m3dLineCombine (const m3dV * __RESTRICT a, const m3dV * __RESTRICT b, float kb, m3dV * __RESTRICT c)
{
   m3dCheckValid(a);
   m3dCheckValid(b);
   m3dCheckValid(kb);

   c->x = a->x + b->x*kb;
   c->y = a->y + b->y*kb;
   c->z = a->z + b->z*kb;
   return;
}

__INLINE void m3dLineCombine (const m3dV * __RESTRICT a, float ka, const m3dV * __RESTRICT b, float kb, m3dV * __RESTRICT c)
{
   m3dCheckValid(a);
   m3dCheckValid(b);
   m3dCheckValid(ka);
   m3dCheckValid(kb);

   c->x = a->x*ka + b->x*kb;
   c->y = a->y*ka + b->y*kb;
   c->z = a->z*ka + b->z*kb;
}

__INLINE void m3dLineCombine (const m4dV * __RESTRICT a, float ka, const m4dV * __RESTRICT b, float kb, m4dV * __RESTRICT c)
{
#ifdef _DEBUG
   m3dCheckValid(a);
   m3dCheckValid(b);
   m3dCheckValid(ka);
   m3dCheckValid(kb);
#endif

   c->x = a->x*ka + b->x*kb;
   c->y = a->y*ka + b->y*kb;
   c->z = a->z*ka + b->z*kb;
   c->w = a->w*ka + b->w*kb;
}


__INLINE void _m2dLineCombine (const m2dV * __RESTRICT a, const m2dV * __RESTRICT b, float kb, m2dV * __RESTRICT c)
{
   m3dCheckValid(a);
   m3dCheckValid(b);
   m3dCheckValid(kb);

   c->x = a->x + b->x*kb;
   c->y = a->y + b->y*kb;
   return;
}

__INLINE void m2dLineCombine (const m2dV * __RESTRICT a, float ka, const m2dV * __RESTRICT b, float kb, m2dV * __RESTRICT c)
{
   m3dCheckValid(a);
   m3dCheckValid(b);
   m3dCheckValid(ka);
   m3dCheckValid(kb);

   c->x = a->x*ka + b->x*kb;
   c->y = a->y*ka + b->y*kb;
}

// Angles between vectors
extern float _m3dAngleVector_COS  (const m3dV *v1, const m3dV *v2);
extern float _m3dAngleVector      (const m3dV *v1, const m3dV *v2);

extern float  m3dAngleVector      (const m3dV *v1, const m3dV *v2 );
extern float  m3dAngleVector_COS  (const m3dV *v1, const m3dV *v2 );

// shortest angle to rotate from v1 to v2 around axis (-180..180)
// !!!!!!!!v1 and v2 must be normal to axis
// if they are not - use m3dAngleVector with 3d param - norm
extern float  m3dAngleVectorAxis  (const m3dV *axis, const m3dV *v1, const m3dV *v2);  

extern float  m3xzAngleVector     (const m3dV *v1, const m3dV *v2 );
extern float  m3xzAngleVector_COS (const m3dV *v1, const m3dV *v2 );

extern float  m3xyAngleVector     (const m3dV *v1, const m3dV *v2 );
extern float  m3xyAngleVector_COS (const m3dV *v1, const m3dV *v2 );

extern float  m3yzAngleVector     (const m3dV *v1, const m3dV *v2 );
extern float  m3yzAngleVector_COS (const m3dV *v1, const m3dV *v2 );

extern float  m3dAngleVector      (const m3dV *v1, const m3dV *v2, const m3dV* norm);

extern float  m2dAngleVector_COS  (const m2dV *v1, const m2dV *v2 );
extern float  m2dAngleVector      (const m2dV *v1, const m2dV *v2 );

extern float  m3dAngleVectorRay_S (const m3dV *rayOrg, const m3dV *rayDir, const m3dV *v1, const m3dV *v2);

// 3D vector mathematics (C = A oper B)
__INLINE void m3dSubtractVector (const m3dV * __RESTRICT a, const m3dV * __RESTRICT b, m3dV * __RESTRICT c)
{
#ifdef _DEBUG
   m3dCheckValid(a);
   m3dCheckValid(b);
#endif

   (c)->x = (a)->x - (b)->x;
   (c)->y = (a)->y - (b)->y;
   (c)->z = (a)->z - (b)->z;
   return;
}

__INLINE void m3dAddVector (const m3dV * __RESTRICT a, const m3dV * __RESTRICT b, m3dV * __RESTRICT c)
{
#ifdef _DEBUG
   m3dCheckValid(a);
   m3dCheckValid(b);
#endif

   (c)->x = (b)->x + (a)->x;
   (c)->y = (b)->y + (a)->y;
   (c)->z = (b)->z + (a)->z;
   return;
}

__INLINE void m3dMultiplyVector (const m3dV * __RESTRICT a, const m3dV * __RESTRICT b, m3dV * __RESTRICT c)
{
#ifdef _DEBUG
   m3dCheckValid(a);
   m3dCheckValid(b);
#endif

   (c)->x = (b)->x * (a)->x;
   (c)->y = (b)->y * (a)->y;
   (c)->z = (b)->z * (a)->z;
   return;
}

__INLINE void m3dScaleVector (m3dV * a, float s)
{
   m3dCheckValid(a);
   m3dCheckValid(s);

   a->x *= s;
   a->y *= s;
   a->z *= s;
   return;
}
__INLINE void m3dScaleVector (const m3dV * __RESTRICT a, float s, m3dV * __RESTRICT b)
{
   m3dCheckValid(a);
   m3dCheckValid(s);

   (b)->x = (a)->x*(s);
   (b)->y = (a)->y*(s);
   (b)->z = (a)->z*(s);
   return;
}
__INLINE void m3dNegateVector (const m3dV * __RESTRICT a, m3dV * __RESTRICT b)
{
   m3dCheckValid(a);

   (b)->x = - (a)->x;
   (b)->y = - (a)->y;
   (b)->z = - (a)->z;
   return;
}

__INLINE void m3dNegateVector (m3dV * __RESTRICT a)
{
   m3dCheckValid(a);

   (a)->x *= -1.f;
   (a)->y *= -1.f;
   (a)->z *= -1.f;
   return;
}


// 3D vector mathematics (A oper= B)
__INLINE void _m3dSubtractVector (m3dV * __RESTRICT a, const m3dV * __RESTRICT b)
{
   m3dCheckValid(a);
   m3dCheckValid(b);

   a->x -= b->x;
   a->y -= b->y;
   a->z -= b->z;
   return;
}

__INLINE void _m3dAddVector (m3dV * __RESTRICT a, const m3dV * __RESTRICT b)
{
   m3dCheckValid(a);
   m3dCheckValid(b);

   a->x += b->x;
   a->y += b->y;
   a->z += b->z;
   return;
}

__INLINE void _m3dMultiplyVector (m3dV * __RESTRICT a, const m3dV * __RESTRICT b)
{
   m3dCheckValid(a);
   m3dCheckValid(b);

   a->x *= b->x;
   a->y *= b->y;
   a->z *= b->z;
   return;
}

__INLINE void _m3dScaleVector (m3dV * __RESTRICT a, float s)
{
   m3dCheckValid(a);
   m3dCheckValid(s);

   a->x *= s;
   a->y *= s;
   a->z *= s;
   return;
}

__INLINE void m3dScaleVector (m4dV * __RESTRICT a, float s)
{
   m3dCheckValid(a);
   m3dCheckValid(s);

   a->x *= s;
   a->y *= s;
   a->z *= s;
   a->w *= s;
   return;
}


__INLINE void _m3dNegateVector (m3dV * __RESTRICT a)
{
   m3dCheckValid(a);

   (a)->x = - (a)->x;
   (a)->y = - (a)->y;
   (a)->z = - (a)->z;
   return;
}

// 2D vector mathematics
__INLINE void m2dSubtractVector (const m2dV * __RESTRICT a, const m2dV * __RESTRICT b, m2dV * __RESTRICT c)
{
   m3dCheckValid(a);
   m3dCheckValid(b);

   c->x = a->x - b->x;
   c->y = a->y - b->y;
   return;
}

__INLINE void m2dAddVector (const m2dV * __RESTRICT a, const m2dV * __RESTRICT b, m2dV * __RESTRICT c)
{
   m3dCheckValid(a);
   m3dCheckValid(b);

   c->x = b->x + a->x;
   c->y = b->y + a->y;
   return;
}

__INLINE void m2dScaleVector (const m2dV * __RESTRICT a, float s, m2dV * __RESTRICT b)
{
   m3dCheckValid(a);
   m3dCheckValid(s);

   b->x = a->x*s;
   b->y = a->y*s;      
   return;
}

__INLINE void m2dNegateVector (const m2dV * __RESTRICT a, m2dV * __RESTRICT b)
{
   m3dCheckValid(a);

   (b)->x = - (a)->x;      
   (b)->y = - (a)->y;      
   return;
}

// 2D vector mathematics (A oper= B)
__INLINE void _m2dSubtractVector (m2dV * __RESTRICT a, const m2dV * __RESTRICT b)
{
   m3dCheckValid(a);
   m3dCheckValid(b);

   a->x -= b->x;   
   a->y -= b->y;   
   return;
}

__INLINE void _m2dAddVector (m2dV * __RESTRICT a, const m2dV * __RESTRICT b)
{
   m3dCheckValid(a);
   m3dCheckValid(b);

   a->x += b->x;   
   a->y += b->y;   
   return;
}

__INLINE void _m2dScaleVector (m2dV * __RESTRICT a, float s)
{
   m3dCheckValid(a);
   m3dCheckValid(s);

   a->x *= s;
   a->y *= s;      
   return;
}
__INLINE void _m2dNegateVector (m3dV * __RESTRICT a)
{
   m3dCheckValid(a);

   (a)->x = - (a)->x;      
   (a)->y = - (a)->y;      
   return;
}

// More vector operations
__INLINE float m2dDotProduct (const m2dV * __RESTRICT a, const m2dV * __RESTRICT b)
{
   m3dCheckValid(a);
   m3dCheckValid(b);

   return a->x*b->x + a->y*b->y;
}

__INLINE void m2dV::Set(float _x, float _y)
{
   x = _x;
   y = _y;
}

__INLINE float m2dV::operator[] (int i) const 
{
   STRONG_ASSERT(i >= 0 && i <= 1);
   return (&x)[i]; 
}

__INLINE float & m2dV::operator[] (int i) 
{
   STRONG_ASSERT(i >= 0 && i <= 1);
   return (&x)[i];
}

__INLINE m2dV & m2dV::operator+= (const m2dV& v)
{
   m3dCheckValid(&v);
   m3dCheckValid(this);
   const m2dV * __RESTRICT pv = &v;
   x += pv->x;
   y += pv->y;
   return *this;
}

__INLINE m2dV & m2dV::operator-= (const m2dV& v)
{
   m3dCheckValid(&v);
   m3dCheckValid(this);
   const m2dV * __RESTRICT pv = &v;
   x -= pv->x;
   y -= pv->y;
   return *this;
}

__INLINE m2dV & m2dV::operator/= (float f)
{
   m3dCheckValid(f);
   f = 1.0f / f;
   x *= f;
   y *= f;
   m3dCheckValid(this);
   return *this;
}

__INLINE m2dV m2dV::operator- (void) const
{ 
   m3dCheckValid(this);
   m2dV  res;
   res.x = -x;
   res.y = -y;
   return res;
}

__INLINE m2dV operator+ (const m2dV& u, const m2dV& v) 
{ 
   m3dCheckValid(&u);
   m3dCheckValid(&v);
   m2dV  res;
   const m2dV * __RESTRICT pu = &u;
   const m2dV * __RESTRICT pv = &v;
   res.x = pu->x + pv->x;
   res.y = pu->y + pv->y;
   return res;
}

__INLINE m2dV operator- (const m2dV& u, const m2dV& v) 
{ 
   m3dCheckValid(&u);
   m3dCheckValid(&v);
   m2dV  res;
   const m2dV * __RESTRICT pu = &u;
   const m2dV * __RESTRICT pv = &v;
   res.x = pu->x - pv->x;
   res.y = pu->y - pv->y;
   return res;
}

__INLINE m2dV operator* (float f, const m2dV& v)
{ 
   m3dCheckValid(f);
   m3dCheckValid(&v);
   m2dV  res;
   const m2dV * __RESTRICT pv = &v;
   res.x = f * pv->x;
   res.y = f * pv->y;
   return res;
}

__INLINE m2dV operator/ (const m2dV& v, float f)
{ 
   m3dCheckValid(f);
   m3dCheckValid(&v);
   m2dV  res;
   const m2dV * __RESTRICT pv = &v;
   res.x = pv->x / f;
   res.y = pv->y / f;
   return res;
}

__INLINE m2dV operator* (const m2dV& v, float f)
{ 
   m3dCheckValid(f);
   m3dCheckValid(&v);
   m2dV  res;
   const m2dV * __RESTRICT pv = &v;
   res.x = f * pv->x;
   res.y = f * pv->y;
   return res;
}

__INLINE m2dV & m2dV::operator*= (float f)
{
   m3dCheckValid(f);
   x *= f;
   y *= f;
   m3dCheckValid(this);
   return *this;
}

__INLINE float m3dDotProduct (const m3dV * __RESTRICT a, const m3dV * __RESTRICT b)
{
#ifdef _DEBUG
   m3dCheckValid(a);
   m3dCheckValid(b);
#endif // _DEBUG
   return a->x*b->x + a->y*b->y + a->z*b->z;
}

__INLINE void m3dCrossProduct (const m3dV * __RESTRICT a, const m3dV * __RESTRICT b, m3dV * __RESTRICT c)
{
   m3dCheckValid(a);
   m3dCheckValid(b);

   c->x = a->y*b->z - a->z*b->y;   
   c->y = a->z*b->x - a->x*b->z;   
   c->z = a->x*b->y - a->y*b->x;
   return;
}

__INLINE void __CDECL m3dNormalize (m3dV * __RESTRICT v)
{
   m3dCheckValid(v);

#if defined _AP_XENON
   XMVECTOR V = XMLoadVector3(v);
   XMVECTOR VUnityY = XMLoadVector3(&m3dVUnitY);
   XMVECTOR D;
   XMVECTOR RcpLength;
   XMVECTOR OneHalfLengthSq;
   XMVECTOR Scale;
   XMVECTOR Result;
   XMVECTOR Zero;
   XMVECTOR H;
   XMVECTOR N;

   H = __vspltisw(1);
   D = __vmsum3fp(V, V);
   H = __vcfsx(H, 1);
   Zero = __vspltisw(0);
   RcpLength = __vrsqrtefp(D);
   OneHalfLengthSq = __vmulfp(D, H);
   Result = __vcmpeqfp(D, Zero);
   Scale = __vmulfp(RcpLength, RcpLength);
   H = __vnmsubfp(OneHalfLengthSq, Scale, H);
   H = __vmaddcfp(RcpLength, H, RcpLength);
   N = __vmulfp(V, H);
   Result = __vsel(N, VUnityY, Result);

   XMStoreVector3(v, Result);

   return ;
#else

#if defined M3D_USE_SSE
   static float three = 3.0f;
   static float half = 0.5f;

      __asm {
         mov         eax, v
         movss       xmm7, M3D_EPSILON_2
         movss       xmm0, dword ptr [eax]
         movss       xmm6, dword ptr [eax+4]
         movss       xmm5, dword ptr [eax+8]
         movaps      xmm2, xmm0
         mulss       xmm2, xmm2
         movaps      xmm3, xmm6
         mulss       xmm3, xmm3
         addss       xmm2, xmm3
         movaps      xmm3, xmm5
         mulss       xmm3, xmm3
         movaps      xmm4, xmm3
         movaps      xmm3, xmm2
         addss       xmm3, xmm4
         comiss      xmm3, xmm7
         jb          _err
         movss       xmm1, three                // x1 = 3
         movss       xmm2, half                 // x2 = .5
         rsqrtss     xmm4, xmm3                 // x4 = apr(1 / sqrt(len))
         mulss       xmm3, xmm4                 // m3 = apr(sqrt(len))
         mulss       xmm2, xmm4                 // m2 = apr(1 / sqrt(len)) / 2
         mulss       xmm3, xmm4                 // m3 = apr(1)
         subss       xmm1, xmm3                 // m1 = 3  - apr(1)
         mulss       xmm2, xmm1                 // m2 = apr(1 / sqrt(len)) / 2 * (3 - apr(1))
         mulss       xmm0, xmm2
         mulss       xmm6, xmm2
         movss       dword ptr [eax],xmm0
         mulss       xmm5, xmm2
         movss       dword ptr [eax+4],xmm6
         movss       dword ptr [eax+8],xmm5
      }

   return;

#else
   float d = m3dPow2(v->x) + m3dPow2(v->y) + m3dPow2(v->z);
   float ood;

   d = m3dSqrt(d);
   if (d < M3D_EPSILON) {
      goto _err;
   }
   ood = 1.f / d;
   v->x *= ood;
   v->y *= ood;
   v->z *= ood;

   return;
#endif //M3D_USE_SSE

_err:
   ASSERT(FALSE);
   v->x = 0.0f;
   v->y = 1.0f;
   v->z = 0.0f;
   return;

#endif // defined(_AP_PC) || defined(_AP_XBOX)
}

__INLINE m3dV __CDECL m3dNormalizedCopy (const m3dV * __RESTRICT v)
{
   m3dV tmp = *v;
   m3dNormalize(&tmp);
   return tmp;
}

__INLINE BOOL __CDECL _m3dNormalize (m3dV * __RESTRICT v)
{
   m3dCheckValid(v);

#if defined _AP_XENON
   {
      XMVECTOR V = XMLoadVector3(v);
      XMVECTOR D;
      XMVECTOR RcpLength;
      XMVECTOR OneHalfLengthSq;
      XMVECTOR Scale;
      XMVECTOR Zero;
      XMVECTOR H;
      XMVECTOR N;

      H = __vspltisw(1);
      D = __vmsum3fp(V, V);
      H = __vcfsx(H, 1);
      Zero = __vspltisw(0);
      RcpLength = __vrsqrtefp(D);
      OneHalfLengthSq = __vmulfp(D, H);   
      Scale = __vmulfp(RcpLength, RcpLength);
      H = __vnmsubfp(OneHalfLengthSq, Scale, H);
      H = __vmaddcfp(RcpLength, H, RcpLength);
      N = __vmulfp(V, H);


      if (XMVector3Equal(D, Zero)) {
         return FALSE;
      } else {
         XMStoreVector3(v, N);
         return TRUE;
      }
   }
#elif (defined(_AP_PC) || defined(_AP_XBOX)) && defined M3D_USE_SSE

   static float three = 3.0f;
   static float half = 0.5f;

   __asm {
      mov         eax, v
      movss       xmm7, M3D_EPSILON_2
      movss       xmm0, dword ptr [eax]
      movss       xmm6, dword ptr [eax+4]
      movss       xmm5, dword ptr [eax+8]
      movaps      xmm2, xmm0
      mulss       xmm2, xmm2
      movaps      xmm3, xmm6
      mulss       xmm3, xmm3
      addss       xmm2, xmm3
      movaps      xmm3, xmm5
      mulss       xmm3, xmm3
      movaps      xmm4, xmm3
      movaps      xmm3, xmm2
      addss       xmm3, xmm4
      comiss      xmm3, xmm7
      jb          _err
      movss       xmm1, three                // x1 = 3
      movss       xmm2, half                 // x2 = .5
      rsqrtss     xmm4, xmm3                 // x4 = apr(1 / sqrt(len))
      mulss       xmm3, xmm4                 // m3 = apr(sqrt(len))
      mulss       xmm2, xmm4                 // m2 = apr(1 / sqrt(len)) / 2
      mulss       xmm3, xmm4                 // m3 = apr(1)
      subss       xmm1, xmm3                 // m1 = 3  - apr(1)
      mulss       xmm2, xmm1                 // m2 = apr(1 / sqrt(len)) / 2 * (3 - apr(1))
      mulss       xmm0, xmm2
      mulss       xmm6, xmm2
      movss       dword ptr [eax],xmm0
      mulss       xmm5, xmm2
      movss       dword ptr [eax+4],xmm6
      movss       dword ptr [eax+8],xmm5
   }

   return TRUE;
_err:
   return FALSE;

#else //FPU version

   float d = m3dPow2(v->x) + m3dPow2(v->y) + m3dPow2(v->z);
   float ood;
           
   d = m3dSqrt(d);
   if (d < M3D_EPSILON) {
      return FALSE;
   }
   ood = 1.f / d;
   v->x *= ood;
   v->y *= ood;
   v->z *= ood;

   return TRUE;

#endif
}

__INLINE BOOL m2dNormalize (m2dV * __RESTRICT v)
{
   float d = m3dSqrt(m3dPow2(v->x) + m3dPow2(v->y));
   float ood;

   m3dCheckValid(v);

   if (m3dFAbs(d) < M3D_EPSILON) {
      ASSERT(FALSE);
      return FALSE;
   }

   ood = 1.f / d;
   v->x *= ood;
   v->y *= ood;
   return TRUE;
}

__INLINE m2dV m2dNormalizedCopy(const m2dV * __RESTRICT v)
{
   m2dV tmp = *v;
   if ( !m2dNormalize(&tmp) ) {
      tmp.x = 1;
      tmp.y = 0;
   }
   return tmp;
}

__INLINE BOOL _m2dNormalize (m2dV * __RESTRICT v)
{
   float d = m3dSqrt(m3dPow2(v->x) + m3dPow2(v->y));
   float ood;

   m3dCheckValid(v);

   if (m3dFAbs(d) < M3D_EPSILON) {
      return FALSE;
   }

   ood = 1.f / d;
   v->x *= ood;
   v->y *= ood;
   return TRUE;
}


/*************************************************************
'* m2dDistPtToSegment( const m2dV* p, const m2dV *s0, const m2dV *s1, m2dV* pNearest )
'* Parameter: const m2dV* p   point from which the distance is computed
'* Parameter: const m2dV *s0  start point of segment
'* Parameter: const m2dV *s1  end point of segment
'* Parameter: m2dV* pNearest  OUT: closest point to p
'* 
'* shortest distance between point and segment
'************************************************************/
__INLINE float m2dDistPtToSegment (const m2dV* __RESTRICT  p, const m2dV * __RESTRICT s0, const m2dV * __RESTRICT s1, m2dV* __RESTRICT  pClosest)
{
   m3dCheckValid(p);
   m3dCheckValid(s0);
   m3dCheckValid(s1);   

   m2dV v; m2dSubtractVector(s1, s0, &v);
   m2dV w; m2dSubtractVector(p, s0, &w);

   m2dV pTmp; 
   if(!pClosest)
      pClosest = &pTmp;

   // is s0 shortest point to p ?
   float c1 = m2dDotProduct(&w, &v);
   if(c1 <= 0){
      *pClosest = *s0;
      return m2dDist(p, s0);
   }

   // is s1 shortest point to p ?
   float c2 = m2dDotProduct(&v, &v);
   if (c2 <= c1){
      *pClosest = *s1;
      return m2dDist(p, s1);
   }   

   // pClosest = projection of p to s0-s1
   float b = c1 / c2;
   m2dLineCombine(s0, 1.0f, &v, b, pClosest);   

   return m2dDist(p, pClosest);
}


// Vertex list normalization
void m3dNormalizeVertList (int nVert, m3dV *vertList);
void m3dNormalizeVertListFast (int nVert, m3dV *vertList);  // no zero vectors check, approximate sqrt


// Get direction from one point towards another
__INLINE BOOL m3dGetDir (const m3dV * __RESTRICT org, const m3dV * __RESTRICT target, m3dV * __RESTRICT dir)
{
   m3dSubtractVector(target, org, dir);
   return _m3dNormalize(dir);
}

// project a vector onto another one
__INLINE float m3dProjVector (const m3dV * __RESTRICT baseV, const m3dV * __RESTRICT v)
{
   m3dCheckValid(baseV);
   m3dCheckValid(v);

   return m3dLengthVector(v)*m3dAngleVector_COS(baseV, v);
}

// vector decomposition by basis
extern void  m2dBasisCoord   (m2dV *va, m2dV *vb, m2dV *v, float *ka, float *kb);
extern void  m3dBasisCoord   (m3dV *va, m3dV *vb, m3dV *v, float *ka, float *kb);
extern void  m3xzBasisCoord  (m3dV *va, m3dV *vb, m3dV *v, float *ka, float *kb);

#define m2dIsVZero(pV)       (m3dIsZero(m2dLengthVector(pV)))
#define m2dIsVZero_A(pV,acc) (m3dIsZero_A(m2dLengthVector(pV),acc))
#define m3dIsVZero(pV)       (m3dIsZero_A(m3dLengthVector_2(pV), M3D_EPSILON_2))
#define m3dIsVZero_A(pV,acc) (m3dIsZero_A(m3dLengthVector(pV),acc))

/*************************************************************
* m3dMakeVX ()
* m3dMakeVY()
* m3dMakeVZ()
* m3dMakeVZero()
*
* Make vectors collinear to the WCS
*************************************************************/
__INLINE void m3dMakeVX (m3dV * __RESTRICT pV, float len)
{
   pV->y = pV->z = 0.f;
   pV->x = len;
   return;
}
__INLINE void m3dMakeVY (m3dV * __RESTRICT pV, float len)
{
   pV->x = pV->z = 0.f;
   pV->y = len;
   return;
}
__INLINE void m3dMakeVZ (m3dV * __RESTRICT pV, float len)
{
   pV->x = pV->y = 0.f;
   pV->z = len;
   return;
}
__INLINE void m3dMakeVZero (m3dV * __RESTRICT pV)
{
   pV->x = pV->y = pV->z = 0.f;
   return;
}
__INLINE void m3dMakeVector (m3dV * __RESTRICT pV, float x, float y, float z)
{
   pV->x = x;
   pV->y = y;
   pV->z = z;
   return;
}

__INLINE void m2dMakeVector (m2dV * __RESTRICT pV, float x, float y)
{
   pV->x = x;
   pV->y = y;
   return;
}

/*************************************************************
* m3dRotatePointAroundRay ()
*
* Rotate a point around a ray by an angle
*
* IN  : org / dir - ray
*       angle     - rotation angle
*       p         - point to rotate
* OUT : pResult   - rotated point  
*************************************************************/
__INLINE void m3dRotatePointAroundRay (m3dV const * __RESTRICT org, m3dV const * __RESTRICT dir, float angle, m3dV const * __RESTRICT p, m3dV * __RESTRICT pResult)
{
   m3dV rot, axisX, axisY;

   m3dSubtractVector(p, org, &rot);

   m3dScaleVector(dir, m3dDotProduct(dir, &rot), &axisX);
   m3dSubtractVector(&rot, &axisX, &axisY);
   
   m3dV cross;
   m3dCrossProduct(dir, &axisY, &cross);

   float sinAngle  = m3dSin(m3dDeg2Rad(angle));
   float cosAngle  = m3dCos(m3dDeg2Rad(angle));

   _m3dScaleVector(&axisY, cosAngle);
   _m3dScaleVector(&cross, sinAngle);

   m3dAddVector(org, &axisY, pResult);
   m3dAddVector(pResult, &cross, pResult);
   m3dAddVector(pResult, &axisX, pResult);
}

/****************************************************************
* m3dNormalComponent ()
*
* IN:  v     - vector
*      norm  - normal
* OUT: vNorm - normal component of 'v' along norm
*
* Get normal component of a given vector along the specified normal
****************************************************************/
__INLINE void m3dNormalComponent (const m3dV * __RESTRICT v, const m3dV * __RESTRICT norm, m3dV * __RESTRICT vNorm)
{
   float prod;

   ASSERT(m3dIsZero(m3dLengthVector(norm)-1.f));

   prod = m3dDotProduct(v,norm);
   m3dScaleVector(norm,prod,vNorm);
   return;
}

/****************************************************************
* m3dTangentComponent ()
*
* IN:  v     - vector
*      norm  - normal
* OUT: vTang - tangential component of 'v' (parallel to a plane
*              having normal 'norm')
*
* Get tangential component of a given vector parallel to a plane 
* specified by normal
****************************************************************/
__INLINE void m3dTangentComponent (const m3dV * __RESTRICT v, const m3dV * __RESTRICT norm, m3dV * __RESTRICT vTang)
{
   m3dV  vNorm;

   m3dNormalComponent(v,norm,&vNorm);
   m3dSubtractVector(v,&vNorm,vTang);
   return;
}

__INLINE float m3dV::operator[] (int i) const 
{
   STRONG_ASSERT(i >= 0 && i <= 2);
   return (&x)[i]; 
}

__INLINE float & m3dV::operator[] (int i) 
{
   STRONG_ASSERT(i >= 0 && i <= 2);
   return (&x)[i]; 
}

__INLINE m3dV & m3dV::operator+= (const m3dV& v)
{
   m3dCheckValid(&v);
   m3dCheckValid(this);
   const m3dV * __RESTRICT pv = &v;
   x += pv->x;
   y += pv->y;
   z += pv->z;
   return *this;
}

__INLINE m3dV & m3dV::operator-= (const m3dV& v)
{
   m3dCheckValid(&v);
   m3dCheckValid(this);
   const m3dV * __RESTRICT pv = &v;
   x -= pv->x;
   y -= pv->y;
   z -= pv->z;
   return *this;
}

__INLINE m3dV & m3dV::operator-= (float f)
{
   m3dCheckValid(f);
   x -= f;
   y -= f;
   z -= f;
   m3dCheckValid(this);
   return *this;
}

__INLINE m3dV operator/ (const m3dV &u, const m3dV &v)
{
   m3dCheckValid(&u);
   m3dCheckValid(&v);
   m3dV res;
   const m3dV * __RESTRICT pu = &u;
   const m3dV * __RESTRICT pv = &v;
   res.x = pu->x / pv->x;
   res.y = pu->y / pv->y;
   res.z = pu->z / pv->z;
   m3dCheckValid(&res);
   return res;
}

__INLINE m3dV operator/(const m3dV &u, float f)
{
   m3dCheckValid(&u);
   m3dCheckValid(f);
   m3dV res;
   res.x = u.x / f;
   res.y = u.y / f;
   res.z = u.z / f;
   m3dCheckValid(&res);
   return res;
}

__INLINE m3dV & m3dV::operator/= (float f)
{
   m3dCheckValid(f);
   f = 1.0f / f;
   x *= f;
   y *= f;
   z *= f;
   m3dCheckValid(this);
   return *this;
}


__INLINE m3dV m3dV::operator- (void) const
{ 
   m3dCheckValid(this);
   m3dV  res;
   res.x = -x;
   res.y = -y;
   res.z = -z;
   return res;
}

__INLINE m3dV operator+ (const m3dV& u, const m3dV& v) 
{ 
   m3dCheckValid(&u);
   m3dCheckValid(&v);
   m3dV  res;
   const m3dV * __RESTRICT pu = &u;
   const m3dV * __RESTRICT pv = &v;
   res.x = pu->x + pv->x;
   res.y = pu->y + pv->y;
   res.z = pu->z + pv->z;
   return res;
}

__INLINE m3dV operator- (const m3dV& u, const m3dV& v) 
{ 
   m3dCheckValid(&u);
   m3dCheckValid(&v);
   m3dV  res;
   const m3dV * __RESTRICT pu = &u;
   const m3dV * __RESTRICT pv = &v;
   res.x = pu->x - pv->x;
   res.y = pu->y - pv->y;
   res.z = pu->z - pv->z;
   return res;
}

__INLINE m3dV operator* (const m3dV& u, const m3dV& v) 
{ 
   m3dCheckValid(&u);
   m3dCheckValid(&v);
   m3dV  res;
   const m3dV * __RESTRICT pu = &u;
   const m3dV * __RESTRICT pv = &v;
   res.x = pu->x * pv->x;
   res.y = pu->y * pv->y;
   res.z = pu->z * pv->z;
   return res;
}

__INLINE m3dV operator* (float f, const m3dV& v)
{ 
   m3dCheckValid(f);
   m3dCheckValid(&v);
   m3dV  res;
   const m3dV * __RESTRICT pv = &v;
   res.x = f * pv->x;
   res.y = f * pv->y;
   res.z = f * pv->z;
   return res;
}

__INLINE m3dV operator* (const m3dV& v, float f)
{ 
   m3dCheckValid(f);
   m3dCheckValid(&v);
   m3dV  res;
   const m3dV * __RESTRICT pv = &v;
   res.x = f * pv->x;
   res.y = f * pv->y;
   res.z = f * pv->z;
   return res;
}

__INLINE m3dV & m3dV::operator*= (float f)
{
   m3dCheckValid(f);
   x *= f;
   y *= f;
   z *= f;
   m3dCheckValid(this);
   return *this;
}

__INLINE void m3dV::Set(float _x, float _y, float _z)
{ 
   x = _x;
   y = _y;
   z = _z;
}

__INLINE bool m3dV::operator < (const m3dV & p) const
{
   if (x != p.x) {
      return x < p.x;
   }
   if (y != p.y) {
      return y < p.y;
   }
   return z < p.z;
}

__INLINE bool m4dV::operator < (const m4dV & p) const
{
   if (x != p.x) {
      return x < p.x;
   }
   if (y != p.y) {
      return y < p.y;
   }
   if (z != p.z) {
      return z < p.z;
   }
   return w < p.w;
}

__INLINE bool m3dVTX::operator < (const m3dVTX & p) const
{
   return s < p.s || s == p.s && t < p.t;
}

__INLINE bool m3dV::operator == (const m3dV & p) const
{
   return x == p.x && y == p.y && z == p.z;
}

__INLINE bool m3dV::operator != (const m3dV & p) const
{
   return x != p.x || y != p.y || z != p.z;
}

__INLINE bool m2dV::operator < (const m2dV & p) const
{
   if (x != p.x) {
      return x < p.x;
   }
   return y < p.y;
}

__INLINE bool m2dV::operator == (const m2dV & p) const
{
   return x == p.x && y == p.y;
}

__INLINE bool m2dV::operator != (const m2dV & p) const
{
   return x != p.x || y != p.y;
}


extern void m3dGetPerpVector(m3dV const*v, m3dV *vPerp);
extern void m2dGetPerpVector(m2dV const*v, m2dV *vPerp);
extern void m3dGetTangent   (m3dV *norm, m3dV *tang);

__INLINE m3dV m3dGetPerpVector(m3dV const*v)
{
   m3dV tmp;
   m3dGetPerpVector(v, &tmp);
   return tmp;
}

__INLINE m2dV m2dGetPerpVector(m2dV const*v)
{
   m2dV tmp;
   m2dGetPerpVector(v, &tmp);
   return tmp;
}

extern void m3dMirrorPointPlane (m3dV *planePoint, m3dV *planeNorm, m3dV *point, m3dV *mirror);
extern void m3dReflectVector    (m3dV *vNormal, m3dV *vIncident, m3dV *vReflect);
extern void m3dWCS2SphereCoord  (m3dV *v, float *rho, float *phi);
extern void m3dSphereCoord2WCS  (float radius, float rho, float phi, m3dV *v);

// Vector clamping & lerping
extern BOOL m3dClampVector_L   (m3dV *vSrc, m3dV *vDest, float maxLen, m3dV *vOut);
extern BOOL m3dClampVector_L   (m3dV *vSrc, m3dV *vDest, float maxLen);
extern BOOL m3dClampVector_A   (m3dV *vSrc, const m3dV *vDest, float maxAngle, m3dV *vOut);
extern BOOL m3xzClampVector_A  (m3dV *vSrc, const m3dV *vDest, float stepAngle, m3dV *vOut);

extern void m3dSphereClip      (m3dV *vSrc, float maxLen);
extern void m3dBoxClip         (m3dV *vSrc, float maxComponent);

extern m3dV m3dSphereClippedCopy(const m3dV *vSrc, float maxLen);
extern m3dV m3dBoxClippedCopy(const m3dV *vSrc, float maxLen);

extern void  m3dClampVector    (const m3dV *vA, const m3dV *vB, float a, float b, float t, m3dV *vOut);
extern void  m3dLerpVector_L   (m3dV *vA, m3dV *vB, float a, float b, float t, m3dV *vOut);
extern void  m3dLerpVector_A   (m3dV *vA, m3dV *vB, float a, float b, float t, m3dV *vOut);
extern float m3dLerpRectCorners(m2dV *pBll, m2dV *pFur, m2dV *pFrom, float fBllBll, float fBllFur, float fFurBll, float fFurFur);

__INLINE m3dV _m3dLerp(const m3dV* vA, const m3dV* vB, float t) 
{
   m3dV tmp;
   tmp.x = _m3dLerp(vA->x, vB->x, t);
   tmp.y = _m3dLerp(vA->y, vB->y, t);
   tmp.z = _m3dLerp(vA->z, vB->z, t);
   return tmp;
}

__INLINE m4dV _m3dLerp(const m4dV* vA, const m4dV* vB, float t) 
{
   m4dV tmp;
   tmp.x = _m3dLerp(vA->x, vB->x, t);
   tmp.y = _m3dLerp(vA->y, vB->y, t);
   tmp.z = _m3dLerp(vA->z, vB->z, t);
   tmp.w = _m3dLerp(vA->w, vB->w, t);
   return tmp;
}


// Distance
extern int   m3dGetClosestVert (int nVert, m3dV *vertList, m3dV *v);

// Misc functions
extern void m3dGetCirclePoints (m3dV const*org, m3dV const*norm, float radius, int nPoints, m3dV *pointList);

// vectors collection while not a matrix
class m3dTRIO {
public:
   m3dV vFrom;
   m3dV vTo;
   m3dV vCross;
   m3dTRIO () {}
   m3dTRIO (m3dV *f, m3dV *t, m3dV *c) { vFrom = *f; vTo = *t; vCross = *c; }
};

/**************************************************************
* M3D_IK.H Inverse kinematics math (2D & 3D)
**************************************************************/
extern BOOL m3dIKSolve2D (float l1, float l2, float a, float b, float *x, float *y);
extern BOOL m3dIKSolve3D (float l1, float l2, m3dV *p1, m3dV *p2, m3dV *pole, m3dV *p3);

//
// End of file 'M3D_VECT.H'
//
